# -*- coding: utf-8 -*-
# @Time    : 2024/7/30 17:57
# @Author  : yujiahao
# @File    : 73_fastapi_dependency_test.py
# @description: 测试依赖项


"""
测试时覆盖依赖项：

1. 有些场景下，您可能需要在测试时覆盖依赖项。
   - 即不希望运行原有依赖项（及其子依赖项）。
   - 反之，要在测试期间（或只是为某些特定测试）提供只用于测试的依赖项，并使用此依赖项的值替换原有依赖项的值。

用例：外部服务：

1. 常见实例是调用外部第三方身份验证应用。
   - 向第三方应用发送令牌，然后返回经验证的用户。
   - 但第三方服务商处理每次请求都可能会收费，并且耗时通常也比调用写死的模拟测试用户更长。
   - 一般只要测试一次外部验证应用就够了，不必每次测试都去调用。

2. 此时，最好覆盖调用外部验证应用的依赖项，使用返回模拟测试用户的自定义依赖项就可以了。

使用 app.dependency_overrides 属性：

1. 对于这些用例，FastAPI 应用支持 app.dependency_overrides 属性，该属性是一个字典。
   - 要在测试时覆盖原有依赖项，这个字典的键应当是原依赖项（函数），值是覆盖依赖项（另一个函数）。
   - 这样一来，FastAPI 就会调用覆盖依赖项，不再调用原依赖项。

提示：

1. FastAPI 应用中的任何位置都可以实现覆盖依赖项。
   - 原依赖项可用于路径操作函数、路径操作装饰器（不需要返回值时）、.include_router() 调用等。
   - FastAPI 可以覆盖这些位置的依赖项。

2. 使用 app.dependency_overrides 把覆盖依赖项重置为空字典：
   - app.dependency_overrides = {}

3. 如果只在某些测试时覆盖依赖项，您可以在测试开始时（在测试函数内）设置覆盖依赖项，并在结束时（在测试函数结尾）重置覆盖依赖项。
"""

from typing import Union
from fastapi import Depends, FastAPI
from fastapi.testclient import TestClient

# 创建 FastAPI 应用实例
app = FastAPI()


# 定义一个依赖项函数，提供常见的查询参数
async def common_parameters(
        q: Union[str, None] = None, skip: int = 0, limit: int = 100
):
    return {"q": q, "skip": skip, "limit": limit}


# 定义一个 GET 路由处理函数，使用 common_parameters 作为依赖项
@app.get("/items/")
async def read_items(commons: dict = Depends(common_parameters)):
    return {"message": "Hello Items!", "params": commons}


# 定义另一个 GET 路由处理函数，使用相同的依赖项
@app.get("/users/")
async def read_users(commons: dict = Depends(common_parameters)):
    return {"message": "Hello Users!", "params": commons}


# 创建一个 FastAPI 测试客户端
client = TestClient(app)


# 定义一个新的依赖项函数，用于覆盖原有的 common_parameters
async def override_dependency(q: Union[str, None] = None):
    return {"q": q, "skip": 5, "limit": 10}


# 使用 app.dependency_overrides 覆盖 common_parameters 依赖项
app.dependency_overrides[common_parameters] = override_dependency


# 定义测试函数，测试覆盖依赖项后的 /items/ 路由
def test_override_in_items():
    # 发送 GET 请求到 /items/
    response = client.get("/items/")
    # 断言响应状态码为 200
    assert response.status_code == 200
    # 断言响应 JSON 数据与预期值匹配
    assert response.json() == {
        "message": "Hello Items!",
        "params": {"q": None, "skip": 5, "limit": 10},
    }


# 定义测试函数，测试覆盖依赖项后的 /items/ 路由，带有查询参数 q
def test_override_in_items_with_q():
    # 发送 GET 请求到 /items/?q=foo
    response = client.get("/items/?q=foo")
    # 断言响应状态码为 200
    assert response.status_code == 200
    # 断言响应 JSON 数据与预期值匹配
    assert response.json() == {
        "message": "Hello Items!",
        "params": {"q": "foo", "skip": 5, "limit": 10},
    }


# 定义测试函数，测试覆盖依赖项后的 /items/ 路由，带有多个查询参数
def test_override_in_items_with_params():
    # 发送 GET 请求到 /items/?q=foo&skip=100&limit=200
    response = client.get("/items/?q=foo&skip=100&limit=200")
    # 断言响应状态码为 200
    assert response.status_code == 200
    # 断言响应 JSON 数据与预期值匹配，即使传入了不同的 skip 和 limit 参数，返回值仍然是覆盖后的默认值
    assert response.json() == {
        "message": "Hello Items!",
        "params": {"q": "foo", "skip": 5, "limit": 10},
    }
